Skip to content

fix(drivers): 聚合函数拒收带上 ADR-0112 信封,并把两类条件分开措辞 (#5907) - #6204

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5907-aggregate-function-envelopes
Aug 7, 2026
Merged

fix(drivers): 聚合函数拒收带上 ADR-0112 信封,并把两类条件分开措辞 (#5907)#6204
os-zhuang merged 2 commits into
mainfrom
claude/issue-5907-aggregate-function-envelopes

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5907

前提复核(先证后改)

origin/main @ 80f7dc6a3 双面实测,两处裸 Error 都还在(行号已漂到 sql-driver.ts:7071 / remote-transport.ts:726,与派发一致),两类输入的现行为如下:

REMOTE median          -> THREW code=undefined status=undefined msg="Unsupported aggregate function: median"
LOCAL  median          -> THREW code=undefined status=undefined msg="Unsupported aggregate function: median"
REMOTE count_distinct  -> THREW code=undefined status=undefined msg="Unsupported aggregate function: count_distinct"
LOCAL  count_distinct  -> THREW code=undefined status=undefined msg="Unsupported aggregate function: count_distinct"
REMOTE array_agg       -> THREW code=undefined status=undefined msg="Unsupported aggregate function: array_agg"
LOCAL  array_agg       -> THREW code=undefined status=undefined msg="Unsupported aggregate function: array_agg"
REMOTE string_agg      -> THREW code=undefined status=undefined msg="Unsupported aggregate function: string_agg"
LOCAL  string_agg      -> THREW code=undefined status=undefined msg="Unsupported aggregate function: string_agg"
REMOTE count           -> RESOLVED  SELECT count("stage") AS "n" FROM "deal"
LOCAL  count           -> RESOLVED  [{"n":0}]

前提成立,两类今天确实同一句话,code/statusundefined

改了什么

两面同改,同一信封体例、首句逐字一致(#5240),体例取 PR #5921(invalidFilterError 形态):

条件 code / status 首句
第 1 类:协议未声明的函数名(median) INVALID_QUERY / 400 Aggregate function "median" is not a declared aggregate function.
第 2 类:协议已声明、本后端编不出(count_distinct / array_agg / string_agg) NOT_IMPLEMENTED / 501 Aggregate function "count_distinct" is declared but not implemented by this backend.

「已声明」的集合从 @objectstack/specAggregationFunction 读,不在驱动里复述。

第 2 类为什么选 NOT_IMPLEMENTED / 501(裁决要求说明理由)

目录里四个候选,按既有语义与 status 映射实测比较:

候选 归属 仓内既有语义 / status 判定
NOT_IMPLEMENTED 标准目录(StandardErrorCode) 注释即「Feature not yet implemented」;HttpStatusErrorCodeMap[501] 就是它;仓内 20+ 处 501 + 「not supported by this protocol/runtime」 选它
UNSUPPORTED 扩展账本(runtime / plugin-sharing) 两处发射点都是 400「这个分享链接不暴露消息」——资源配置不暴露,语义已被占
UNSUPPORTED_QUERY_PARAM 扩展账本(metadata-protocol) 400,且在 isExpectedQueryRejection 的「调用方点错参数名」清单上 否,方向相反
UNSUPPORTED_TRANSFORM 扩展账本(@objectstack/rest) 导入映射器的 transform,领域不符

另两条支撑:账本自己的规矩是「条件通用就用标准目录,不要注册同义词」,而驱动包今天在账本里没有条目(注册反而要新开一行);标准目录 + HttpStatusErrorCodeMap 让 code 与 status 互为镜像,不是本函数自己挑的。

第 1 类的 INVALID_QUERY 同理不是新拼法:协议门对同一条件已经在给这个码 —— metadata-protocolinvalidQueryError 对「aggregations 轴上一个枚举外的 function」refuse 的就是 400 INVALID_QUERY(#4254)。于是进程内调用方与 REST 调用方读到同一个答案。

一处被迫做的判断:按调用方写的原始拼写分类

两面的归一化本来就不一样(remote 先 .toLowerCase(),local 不动),实测:

COUNT           REMOTE -> RESOLVED "SELECT count(...)"        LOCAL -> THREW
COUNT_DISTINCT  REMOTE -> THREW "…: count_distinct"           LOCAL -> THREW "…: COUNT_DISTINCT"

若各面按自己归一化之后的名字分类,COUNT_DISTINCT 会在 remote 拿 501、在 local 拿 400 —— 一条今天两面答案一致的输入,被本 PR 劈成新的叉,正是本单要关的东西。所以分类一律判调用方写的那串字节AggregationFunction(大小写敏感的 z.enum)的成员关系:任何规范拼写两面完全一致;任何错误大小写两面都落第 1 类。归一化差异本身(COUNT 在 remote 编得出)未触碰,如实钉在用例里并另立 #6203

测试

新增两个文件,进两包既有拒收族(命名沿用各自族的惯例):

  • packages/drivers/driver-sql/src/sql-driver-out-of-contract-aggregate-function.test.ts —— 14 例
  • packages/drivers/driver-turso/src/remote-transport-aggregate-function-refusal.test.ts —— 16 例,含 4 条跨包 parity 用例:比对 SqlDriverRemoteTransport 两个运行时消息(不是两份字面量),这半边只改一个包是满足不了的。

⚠️ 每一例都断言 code status,不只断言「它抛了」——未修驱动本来就抛(见上方实测),只断言抛出的用例会恒绿、对本缺陷完全失明(#6144)。

反向验证(方向先判后跑),两个方向都测了

预判:未修驱动对这些输入本来就抛,所以还原裸 Error 后,每条拒收用例应当挂在第一条断言(err.codeundefined),没有一条该走 refusalOf 的「它居然 resolved」分支;控制组保持绿。

实测:

  1. 两面同时还原 —— driver-sql 10 failed / 4 passed(共 14),driver-turso 8 failed / 8 passed(共 16)。全部挂在 expected undefined to be 'INVALID_QUERY' / 'NOT_IMPLEMENTED',无一条挂在「resolved」,与预判一致。
    但预判有一半错了,如实记下:4 条 parity 用例这时是绿的 —— 两面都匿名,就都在 undefined/undefined 上「一致」。parity 测的是一致性,不是正确性。
  2. 只还原一面 —— 最初是踩到 @objectstack/driver-sql 的陈旧 dist/ 意外测到的(本包源码已修、依赖包是旧的):4 条 parity 用例以 expected 'NOT_IMPLEMENTED' to be undefined 全红。

(2) 才是这个文件存在的方向:本单防的不是「某一面沉默」,而是「两面对同一条查询给不同答案」,所以必须变红的是单面改动。两条测量都写进了测试文件的 docblock。

命令与结果

pnpm --workspace-concurrency=2 --filter '@objectstack/driver-sql' --filter '@objectstack/driver-turso' typecheck
  driver-sql typecheck: Done / driver-turso typecheck: Done

driver-sql   全量  Test Files 66 passed | 4 skipped (70)   Tests 920 passed | 46 skipped (966)
driver-turso 全量  Test Files 25 passed (25)               Tests 804 passed (804)
driver-sqlite-wasm 全量(继承 mapAggregateFunc) Test Files 18 passed (18) Tests 254 passed (254)

check:error-code-casing / check:route-envelope / check:driver-conformance /
check:empty-changeset / check:adr-anchors / check:doc-authoring / check:nul-bytes  全绿

消费半径已扫:全仓没有任何测试同时构造 SQL 驱动与这三个函数;Unsupported aggregate function 的旧措辞除本 PR 新增文件外只出现在两处文档,已一并订正(不是 content/docs/releases/)。

边界

顺带记录的发现(未在本 PR 修)


🤖 Generated with Claude Code

https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx


Generated by Claude Code

`SqlDriver.mapAggregateFunc()` 与 `RemoteTransport.aggregate()` 对同一条件各抛
一个裸 `Error`(`code`/`status` 皆 `undefined`),`mapDataError` 因此落默认分支,
一条本该 4xx 的调用方错误以不透明 500 到达客户端。两处同改:

- 协议未声明的函数名(`median`)→ `INVALID_QUERY` / 400,与协议门
  (`metadata-protocol` 的 `invalidQueryError`,#4254)对同一条件已有的码一致。
- 协议已声明、本后端编不出(`count_distinct` / `array_agg` / `string_agg`)→
  `NOT_IMPLEMENTED` / 501,标准目录的能力缺口码;措辞明确说查询拼写无误,
  不把作者说成打错字(#5345 在 driver-memory 上画过的同一条线)。

两面首句逐字一致(#5240),由一条跨包 parity 用例比对两个**运行时**消息钉住。
编得出的五个函数生成的 SQL 逐字节不变;分类按调用方写的原始拼写判定,免得两面
归一化差异顺着信封扩散(#6203)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 11:04am

Request Review

@github-actions github-actions Bot added the size/l label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-sql, @objectstack/driver-turso.

12 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-sql, @objectstack/driver-turso)
  • content/docs/deployment/cli.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/environment-variables.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/driver-turso)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql, @objectstack/driver-turso)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-sql, @objectstack/driver-turso)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-sql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-sql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

`query-options-erasure` 棘轮(#4918)红:新增的两个测试文件里有 5 处 query
options 调用点写成 `as any`,把测试面从 267 抬到 272。基线数字一个没动,5 处
逐一按「该给类型」还是「刻意越契约」重写:

- 第 2 类(`count_distinct` / `array_agg` / `string_agg`)与全部控制组用例走
  `declaredAst()`,`function` 形参类型是 `AggregationNode['function']` —— 于是
  `tsc` 反过来证明这三个名字确实是已声明枚举的成员,正是第 2 类在运行时主张的
  那件事;写错一个字母会在构建期红,而不是悄悄变成一条第 1 类输入却仍然让第 2
  类断言「通过」。
- 第 1 类(`median` / 错误大小写)与「无 alias 默认列名」控制组按定义就构造不出
  `QueryAST`,写 `as unknown as QueryAST`:命名了被绕过的契约、其余键仍受检、
  可 grep 为有意为之,且不计入棘轮。

行为与断言一字未改。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
@os-zhuang
os-zhuang marked this pull request as ready for review August 7, 2026 11:17
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 939f579 Aug 7, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5907-aggregate-function-envelopes branch August 7, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants